import { bus } from "@/bus";
import { loadConfig } from '@/config.ts'
import * as msgpack from 'msgpack-lite';
import * as state from './state'
import { DataSource } from "typeorm";
import { Gateway } from "@/entity/gateway.entity";
import { MqttClient } from "mqtt/*";
import { WristbandAdv } from "@/entity/wristband_adv.entity";
import { Wristband } from "@/entity/wristband.entity";
import { WristbandAdvLast } from "@/entity/wristband_adv_last.entity";
import Redis from "ioredis";
let key_sh: Buffer;
let iv_sh: Buffer;

// 上报给上海
export async function setup(AppDataSource: DataSource | null, clientShanghai: MqttClient, redisClient: Redis) {
    let conf = await loadConfig();
    key_sh = Buffer.from(conf.default.report.crypto_sh.key, "hex");
    iv_sh = Buffer.from(conf.default.report.crypto_sh.iv, "hex");
    
    let gatewayRepo: any;
    if (AppDataSource) {
        gatewayRepo = AppDataSource.getRepository(Gateway);
    }

    // 订阅上游数据 (format格式统一)
    bus.on('wb.adv.decode', async (pack: any) => {
        // console.log('last_report_wb_cnt', pack.wb_adv.length)
        const gw = {
            mac: pack.mac,
            version_bin: pack.frm_ver.split('/')[0], // 固件版本号
            version_fs: pack.frm_ver.split('/')[1], // 文件系统版本号
            time: new Date(),
            last_report_time: new Date(), // 最后上报广播数据的时间
            last_report_wb_cnt: pack.wb_adv.length, // 最后上报手环数据
            deleted: false,
            outdoor: false,
            ip: pack.ip
        }

        const wbArray = pack.wb_adv.map((item: any) => {
            return {
                addr: item.mac.toUpperCase(),
                rssi: item.rssi | -1000000000,
                idx: item.idx,
                beacon1: item.beacon1,
                beacon2: item.beacon2,
                beacon3: item.beacon3,
                acc_x: 0,
                acc_y: 0,
                acc_z: 0,
                steps: item.steps,
                hr: item.hr,
                hrv: item.hrv,
                hr_confidence: item.hr_confidence,
                temperature: item.temp,
                bat_vol: item.bat_vol,
                charging: item.charging,
                wear_on: item.wear_on,
                req_rt: item.req_rt,
                fake_hr: item.fake_hr,
                time: new Date(),
                motion: item.motion || 0, // 运动强度
                motion_variance: item.motion_variance || 0, // 运动方差
                format: item.format_ver,
                version: item.firmware_ver,
                deleted: false,
            }
        });

        let report = {
            v: '', // 蓝牙网关固件版本
            mid: Math.floor(Math.random() * 1000000000), // 消息唯一 ID
            time: 1, // 蓝牙网关设备启动时长
            ip: null, // 蓝牙网关 ip
            mac: null, // 蓝牙网关 mac
            devices: [] // 广播数组
        } as state.ReportObject;

        let devices = [];
        for (const item of wbArray) {
            let val = await messageDevices(item);
            if (val) {
                if (Buffer.byteLength(val) > 0) {
                    devices.push(val);
                }
                report = {
                    v: gw.version_bin + '/' + gw.version_fs,
                    mid: Math.floor(Math.random() * 1000000000),
                    time: 1,
                    ip: gw.ip,
                    mac: item.addr.toUpperCase(),
                    devices: [] // 广播数组
                };
            }
        }
        report.devices = devices;

        let encodeVal = msgpack.encode(report);

        // 查询网关信息，优先从Redis获取
        let gateway;
        try {
            const redisKey = `gateway:${gw.mac}`;
            const cachedGateway = await redisClient.get(redisKey);

            if (cachedGateway) {
                gateway = JSON.parse(cachedGateway);
            } else {
                // 从数据库查询
                if (AppDataSource) {
                    gateway = await getGatewayByMac(gw.mac);
                    if (gateway) {
                        // 存储到Redis
                        await redisClient.setex(redisKey, 3600, JSON.stringify(gateway));
                    }
                }
            }

            // 上报给上海
            if (report.devices.length > 0) {
                // console.log('上报。。。')
                const publishTopic = gateway?.topic || 'shanghaiecs/sh/suzhou/zy/in/100';
                await publishShangHai(publishTopic, encodeVal, clientShanghai);
            }

        } catch (err) {
            console.error('处理网关信息时出错:', err);
            if (report.devices.length > 0) {
                await publishShangHai('shanghaiecs/sh/suzhou/zy/in/100', encodeVal, clientShanghai);
            }
        }

        // 发布数据到上海
        async function publishShangHai(topic: string, message: string | Buffer, clientShanghai: MqttClient) {
            if (!clientShanghai.connected) {
                return;
            }

            clientShanghai.publish(topic, message, { qos: 1 }, (err: any) => {
                if (err) {
                    console.error(`发布消息失败: ${topic}`, err);
                }
            });
        }

        // 查询网关信息
        async function getGatewayByMac(mac: string) {
            if (AppDataSource) {
                const query = gatewayRepo.createQueryBuilder('gateway')
                    .leftJoinAndSelect('gateway.class', 'class')
                    .leftJoinAndSelect('class.school', 'school');

                if (mac) query.andWhere('gateway.mac = :mac', { mac });
                let result = await query.getMany();
                return result[0];
            } else {
                console.log("没有数据库连接，无法查询网关信息");
                return null;
            }
        }

        // 处理设备数据
        async function messageDevices(val: state.WbArray): Promise<Buffer> {
            try {
                let ble = val.addr.replace(/:/g, ''); // 移除冒号
                let rssi = val.rssi || 0;
                let bleBuffer = Buffer.from(ble, 'hex');
                let rssiBuffer = Buffer.from([rssi < 0 ? rssi + 256 : rssi]);
                let prefixBuffer = Buffer.from([0x02, 0x01, 0x06, 0x1A, 0xFF]);
                let buffer = new Uint8Array(4);
                buffer[0] = 0x43;
                buffer[1] = 0x31;
                buffer[2] = 580;
                buffer[3] = val.format || -111111;
                let formatData = await ToformatShanghai(val);
                let formatDataBuffer = Buffer.from(formatData);
                let encryptValBuffer = await encrypt(formatDataBuffer);
                let encryptVal = Buffer.concat([buffer, encryptValBuffer]);
                let resultBuffer = Buffer.concat([Buffer.from([0x00]), bleBuffer, rssiBuffer, prefixBuffer, encryptVal]);
                return resultBuffer;
            } catch (error) {
                console.error('Error in createAdv:', error);
                return Buffer.alloc(0); // 返回空 Buffer
            }
        }

        // 加密
        async function encrypt(buffer: Buffer) {
            const cryptoKey = await crypto.subtle.importKey(
                'raw',
                key_sh,
                { name: 'AES-CTR' },
                false,
                ['encrypt']
            );
            const encryptedBuffer = await crypto.subtle.encrypt(
                { name: 'AES-CTR', counter: iv_sh, length: 128 },
                cryptoKey,
                buffer
            );
            return new Uint8Array(encryptedBuffer);
        }

        async function ToformatShanghai(val: any) {
            let buffer = new Uint8Array(21);
            buffer[0] = (val.steps >> 8) & 0xff;
            buffer[1] = val.steps & 0xff;
            buffer[2] = val.hr;
            buffer[3] = (val.acc_x >> 8) & 0xff;
            buffer[4] = val.acc_x & 0xff;
            buffer[5] = (val.acc_y >> 8) & 0xff;
            buffer[6] = val.acc_y & 0xff;
            buffer[7] = (val.acc_z >> 8) & 0xff;
            buffer[8] = val.acc_z & 0xff;
            buffer[9] = (Math.round(val.temperature * 100) >> 8) & 0xff;
            buffer[10] = Math.round(val.temperature * 100) & 0xff;
            buffer[11] = val.bat_vol;
            buffer[12] = val.charging ? 0x01 : 0x00;
            buffer[13] = val.wear_on ? 0x01 : 0x00;
            buffer[14] = val.motion;
            buffer[15] = val.motion_variance;
            buffer[16] = val.fake_hr;
            buffer[17] = val.hr_confidence;
            buffer[18] = val.time.getMinutes();
            buffer[19] = val.time.getSeconds();
            buffer[20] = val.format;
            return buffer;
        }

    });
}
